﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using RayDen.Library.Components.Surface.Spectral;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;
using RayDen.Library.Data.Imaging;
using RayDen.RayEngine.Core.Interface;
using GlassMaterial = RayDen.Library.Components.Surface.Spectral.GlassMaterial;
using MatteMaterial = RayDen.Library.Components.Surface.Spectral.MatteMaterial;
using MetalMaterial = RayDen.Library.Components.Surface.Spectral.MetalMaterial;

namespace RayDen.RayEngine.Engines.XP
{
    public class SpectralSamplingHelper
    {
        public static int SpectralSamples = 16;
            //SampledSpectrum.nSamples;


 

        public static int SpectralResolution = (SampledSpectrum.sampledLambdaEnd - SampledSpectrum.sampledLambdaStart)/
                                               SpectralSamples;

        private RayEngineScene scene;
        private readonly Dictionary<string, SpectralDistribution> lightDistributions;
        private Dictionary<string, SpectralBrdf> brdfs;
        private SpectralBrdf[] mats;


        public SpectralSamplingHelper(RayEngineScene scene)
        {
            this.scene = scene;
            lightDistributions = new Dictionary<string, SpectralDistribution>();
            brdfs = new Dictionary<string, SpectralBrdf>();

            foreach (var light in scene.Lights)
            {
                lightDistributions.Add(light.Name.ToLowerInvariant(), ColorFactory.CreateRegularSpd(light.Profile.EmissionSpectra));
            }

            foreach (var surfaceMaterial in scene.MatLib.Cast<Library.Components.Surface.SurfaceMaterial>().Where(item => item != null))
            {
                SpectralBrdf brdf = null;
                var materialInfo = surfaceMaterial.First.MaterialData;
                switch (surfaceMaterial.Class)
                {
                    default:
                    case RayDen.Library.Components.Surface.BrdfClass.DiffuseLambert:
                        if (materialInfo != null && materialInfo.Name.ToLowerInvariant().StartsWith("mb") && materialInfo.Name.Length < 5)
                        {
                            var index = int.Parse(materialInfo.Name.Substring(2, materialInfo.Name.Length - 2));
                            float[] mRes = new float[81];
                            for (int i = 0; i < 81; i++)
                            {
                                mRes[i] = (float) SampledSpectrum.macbethSpectra[i][index - 1];
                            }
                            brdf = new MatteMaterial(ColorFactory.CreateRegularFromRegular(mRes));
                        }
                        else
                        {
                            brdf =
                                new MatteMaterial(surfaceMaterial.First.MaterialData != null
                                    ? surfaceMaterial.First.MaterialData.Kd
                                    : new RgbSpectrum(0.5f));
                        }
                        break;
                    case RayDen.Library.Components.Surface.BrdfClass.Glass:
                        brdf = new GlassMaterial(surfaceMaterial.First.MaterialData.Kr,
                            surfaceMaterial.First.MaterialData.Ks, 1.0f, 1.4f, true, true);
                        break;
                    case RayDen.Library.Components.Surface.BrdfClass.GlossyMetal:
                        brdf = new MetalMaterial(surfaceMaterial.First.MaterialData.Kd, surfaceMaterial.First.MaterialData.Exponent, true);
                        break;

                }
                {
                    brdf.SetMaterialInfo(surfaceMaterial.First.MaterialData);
                    brdfs.Add(surfaceMaterial.Name.ToLower(), brdf);
                }
            }
            mats = brdfs.Values.ToArray();
        }


        public float SampleLight(ILight lt, float hw_lambda)
        {
            SpectralDistribution spd = lightDistributions.Count == 1? lightDistributions.First().Value : lightDistributions[lt.Name.ToLowerInvariant()];
            return spd.Sample(hw_lambda);
        }


        public SpectralBrdf GetBrdf(string name)
        {
            if (!brdfs.ContainsKey(name))
                return brdfs.First().Value;

            return brdfs[name];
        }
        public SpectralBrdf GetBrdf(int id)
        {
            return mats[id];
        }

        public float SampleEnvironment(float lambda, ref Vector dir)
        {
            if (scene.EnvironmentMap == null)
                return 0f;

            var rd = scene.EnvironmentMap.SampleBackground(ref dir);
            var spd = ColorFactory.CreateRegularSpd(ref rd, SpectrumType.Illuminant);
            return spd.Sample(lambda);
        }

        FastRandom rnd = new FastRandom();
        public float SampleWavelength(float u0)
        {

            return SampledSpectrum.sampledLambdaStart + u0*(SampledSpectrum.sampledLambdaEnd-SampledSpectrum.sampledLambdaStart);
        }

        public float SampleWavelength(int n)
        {
            return SampledSpectrum.sampledLambdaStart + (n+1)*SpectralResolution;
        }
    }
}
